package com.jp.xml.asn1;

import com.bening.smsapp.util.Asn1Digester;
import com.ibm.asn1.*;
import com.ibm.util.BitString;
import com.ibm.util.Hex;

import java.io.IOException;
import java.io.InputStream;
import java.math.BigInteger;
import java.util.Calendar;
import org.xml.sax.ContentHandler;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.AttributesImpl;


public class ASN12SAX
{
	private static int count = 0;
 public ASN12SAX(ContentHandler handler)
 {
     this.handler = handler;
     isPartial = false;
     encoding = null;
     init();
 }

 public void init()
 {
     decoder = null;
     in = null;
     data = null;
 }

 public void setInputStream(InputStream in)
 {
     this.in = in;
 }

 public void setByteArray(byte data[])
 {
     this.data = data;
 }

 public void setPartialDecoding(boolean isPartial)
 {
     this.isPartial = isPartial;
 }

 public void setEncoding(String encoding)
 {
     this.encoding = encoding;
 }

 public void start()
     throws SAXException
 {
     try
     {
    	 org.xml.sax.Attributes aa = new AttributesImpl();
    	 if(in != null)
             decoder = new BERDecoder(in);
         if(data != null)
             decoder = new BERDecoder(data);
         if(decoder != null)
         {
             if(!isPartial)
                 handler.startDocument();
             
             handler.startElement("", "", "ROOT", aa);
             decode();
             	 
             if(!isPartial)
                 handler.endDocument();
          
         }
     }
     catch(SAXException e)
     {
         throw e;
     }
     catch(Exception e)
     {
         //throw new SAXException(e);
     }
 }

 private void decode()
     throws ASN1Exception, SAXException
 {
     int cl = ASN1Tag.getTagClass(decoder.peekNextTag());
     switch(cl)
     {
     case 0: // '\0'
         try {
        	 while(this.in.available()!=-1) {
        		 decodeUniversal();
        		 
        	 }
         }catch(IOException i) {
        	i.printStackTrace(); 
         }
         break;

     default:
         decodeOther();
         break;
     }
 }

 private void decodeUniversal()
     throws ASN1Exception, SAXException
 {
     int ta = decoder.peekNextTag();
     switch(ta)
     {
     case 1: // '\001'
     case 2: // '\002'
     case 5: // '\005'
     case 6: // '\006'
     case 9: // '\t'
     case 10: // '\n'
         decodeBooleanGroup(ta);
         break;

     case 3: // '\003'
     case 4: // '\004'
     case 7: // '\007'
     case 18: // '\022'
     case 19: // '\023'
     case 20: // '\024'
     case 21: // '\025'
     case 22: // '\026'
     case 23: // '\027'
     case 24: // '\030'
     case 25: // '\031'
     case 26: // '\032'
     case 27: // '\033'
         decodeBitStringGroup(ta);
//         handler.endDocument();
         break;

     case 8: // '\b'
     case 16: // '\020'
     case 17: // '\021'
         decodeSequenceGroup(ta);
         if(count==1085)
        	 System.out.println(count);
         break;

     case 11: // '\013'
     case 12: // '\f'
     case 13: // '\r'
     case 14: // '\016'
     case 15: // '\017'
     default:
         throw new ASN1Exception("Unknown tag: " + ta);
     }
 }

 private void decodeBooleanGroup(int tag)
     throws ASN1Exception, SAXException
 {
     String na = toTypeName(tag);
     org.xml.sax.Attributes aa = new AttributesImpl();
     handler.startElement("", "", na, aa);
     char cc[] = decodeSimpleType(tag);
     if(cc != null)
         handler.characters(cc, 0, cc.length);
     handler.endElement("", "", na);
 }

 private String toTypeName(int tag)
     throws ASN1Exception
 {
     String name = null;
     switch(tag)
     {
     case 1: // '\001'
         name = "_BOOLEAN";
         break;

     case 2: // '\002'
         name = "_INTEGER";
         break;

     case 3: // '\003'
         name = "_BIT_STRING";
         break;

     case 4: // '\004'
         name = "_OCTET_STRING";
         break;

     case 5: // '\005'
         name = "_NULL";
         break;

     case 6: // '\006'
         name = "_OBJECT_IDENTIFIER";
         break;

     case 7: // '\007'
         name = "_ObjectDescriptor";
         break;

     case 8: // '\b'
         name = "_EXTERNAL";
         break;

     case 9: // '\t'
         name = "_REAL";
         break;

     case 10: // '\n'
         name = "_ENUMERATED";
         break;

     case 16: // '\020'
         name = "_SEQUENCE";
         break;

     case 17: // '\021'
         name = "_SET";
         break;

     case 18: // '\022'
         name = "_NumericString";
         break;

     case 19: // '\023'
         name = "_PrintableString";
         break;

     case 20: // '\024'
         name = "_T61String";
         break;

     case 21: // '\025'
         name = "_VideotexString";
         break;

     case 22: // '\026'
         name = "_IA5String";
         break;

     case 23: // '\027'
         name = "_UTCTime";
         break;

     case 24: // '\030'
         name = "_GeneralizedTime";
         break;

     case 25: // '\031'
         name = "_GraphicString";
         break;

     case 26: // '\032'
         name = "_VisibleString";
         break;

     case 27: // '\033'
         name = "_GeneralString";
         break;

     case 11: // '\013'
     case 12: // '\f'
     case 13: // '\r'
     case 14: // '\016'
     case 15: // '\017'
     default:
         throw new ASN1Exception("Unknown tag: " + tag);
     }
     return name;
 }

 private char[] decodeSimpleType(int tag)
     throws ASN1Exception
 {
     char value[] = null;
     switch(tag)
     {
     case 1: // '\001'
     {
         boolean b = decoder.decodeBoolean();
         value = String.valueOf(b).toUpperCase().toCharArray();
         break;
     }

     case 2: // '\002'
     {
         BigInteger bi = decoder.decodeInteger();
         value = bi.toString().toCharArray();
         break;
     }

     case 10: // '\n'
     {
         int i = decoder.decodeEnumeration();
         value = String.valueOf(i).toCharArray();
         break;
     }

     case 9: // '\t'
     {
         double d = decoder.decodeReal();
         value = String.valueOf(d).toCharArray();
         break;
     }

     case 3: // '\003'
     {
         BitString bs = decoder.decodeBitString();
         value = bs.toString().toCharArray();
         break;
     }

     case 4: // '\004'
     {
         byte bb[] = decoder.decodeOctetString();
         value = Hex.toString(bb).toCharArray();
         break;
     }

     case 5: // '\005'
     {
         decoder.decodeNull();
         break;
     }

     case 6: // '\006'
     {
         ASN1OID id = decoder.decodeObjectIdentifier();
         value = id.toString().toCharArray();
         break;
     }

     case 18: // '\022'
     {
         String s = decoder.decodeNumericString();
         value = s.toCharArray();
         break;
     }

     case 19: // '\023'
     {
         String s = decoder.decodePrintableString();
         value = s.toCharArray();
         break;
     }

     case 20: // '\024'
     {
         String s = decoder.decodeT61String();
         value = s.toCharArray();
         break;
     }

     case 21: // '\025'
     {
         String s = decoder.decodeVideotexString();
         value = s.toCharArray();
         break;
     }

     case 26: // '\032'
     {
         String s = decoder.decodeVisibleString();
         value = s.toCharArray();
         break;
     }

     case 22: // '\026'
     {
         String s = decoder.decodeIA5String();
         value = s.toCharArray();
         break;
     }

     case 25: // '\031'
     {
         String s = decoder.decodeGraphicString();
         value = s.toCharArray();
         break;
     }

     case 27: // '\033'
     {
         String s = decoder.decodeGeneralString();
         value = s.toCharArray();
         break;
     }

     case 24: // '\030'
     {
         Calendar c = decoder.decodeGeneralizedTime();
         value = toGeneralizedTime(c).toCharArray();
         break;
     }

     case 23: // '\027'
     {
         Calendar c = decoder.decodeUTCTime();
         value = toUTCTime(c).toCharArray();
         break;
     }

     case 7: // '\007'
     {
         decoder.nextIsImplicit(7);
         String s = decoder.decodeGraphicString();
         value = s.toCharArray();
         break;
     }

     case 8: // '\b'
     case 11: // '\013'
     case 12: // '\f'
     case 13: // '\r'
     case 14: // '\016'
     case 15: // '\017'
     case 16: // '\020'
     case 17: // '\021'
     default:
     {
         throw new ASN1Exception("Unknown tag: " + tag);
     }
     }
     return value;
 }

 private String toGeneralizedTime(Calendar date)
 {
     return toTime(date, true);
 }

 private String toUTCTime(Calendar date)
 {
     return toTime(date, false);
 }

 private String toTime(Calendar date, boolean isGT)
 {
     StringBuffer buf = new StringBuffer();
     String s = String.valueOf(date.get(1));
     int o = isGT ? 4 : 2;
     buf.append(s.substring(s.length() - o));
     s = String.valueOf(date.get(2) + 1);
     buf.append(formatString(s, 2));
     s = String.valueOf(date.get(5));
     buf.append(formatString(s, 2));
     s = String.valueOf(date.get(11));
     buf.append(formatString(s, 2));
     s = String.valueOf(date.get(12));
     buf.append(formatString(s, 2));
     s = String.valueOf(date.get(13));
     buf.append(formatString(s, 2));
     if(isGT)
     {
         String s_;
         for(s_ = String.valueOf(date.get(14)); s_.endsWith("0"); s_ = s_.substring(0, s_.length() - 1));
         if(!s_.equals(""))
             buf.append("." + s_);
     }
     int i = date.get(15);
     if(i == 0)
     {
         buf.append("Z");
     } else
     {
         buf.append(i <= 0 ? "-" : "+");
         i = Math.abs(i);
         s = String.valueOf(i / 0x36ee80);
         buf.append(formatString(s, 2));
         s = String.valueOf((i % 0x36ee80) / 60000);
         buf.append(formatString(s, 2));
     }
     return buf.toString();
 }

 private String formatString(String str, int len)
 {
     StringBuffer buf = new StringBuffer();
     int i = 0;
     for(int j = len - str.length(); i < j; i++)
         buf.append("0");

     buf.append(str);
     return buf.toString();
 }

 private void decodeBitStringGroup(int tag)
     throws ASN1Exception, SAXException
 {
     String na = toTypeName(tag);
     AttributesImpl aa = new AttributesImpl();
     char cc[] = null;
     if(decoder.nextTagConstructed())
     {
         StringBuffer s = new StringBuffer();
         decoder.nextIsImplicit(tag);
         for(int i = decoder.decodeSequence(); !decoder.endOf(i); s.append(cc))
             cc = decodeSimpleType(tag);

         cc = s.toString().toCharArray();
     } else
     {
         cc = decodeSimpleType(tag);
     }
     if(encoding != null)
         switch(tag)
         {
         case 3: // '\003'
             aa.addAttribute("", "", "encoding", "CDATA", encoding);
             aa.addAttribute("", "", "length", "CDATA", String.valueOf(cc.length));
             cc = encodeBitString(cc, encoding);
             break;

         case 4: // '\004'
             aa.addAttribute("", "", "encoding", "CDATA", encoding);
             cc = encodeOctetString(cc, encoding);
             break;
         }
     handler.startElement("", "", na, aa);
     handler.characters(cc, 0, cc.length);
     handler.endElement("", "", na);
 }

 private char[] encodeBitString(char chars[], String enc)
     throws SAXException
 {
     char chars_[] = null;
     if(enc.equals("base64"))
         chars_ = Base64X.encodeBitString(String.valueOf(chars)).toCharArray();
     else
         throw new SAXException("Unknown encoding: " + enc);
     return chars_;
 }

 private char[] encodeOctetString(char chars[], String enc)
     throws SAXException
 {
     char chars_[] = null;
     if(enc.equals("base64"))
         chars_ = Base64X.encodeOctetString(String.valueOf(chars)).toCharArray();
     else
         throw new SAXException("Unknown encoding: " + enc);
     return chars_;
 }

 private void decodeSequenceGroup(int tag)
     throws ASN1Exception, SAXException
 {
     String na = toTypeName(tag);
     org.xml.sax.Attributes aa = new AttributesImpl();
     handler.startElement("", "", na, aa);
     int a = decodeStructuredType(tag);
     count++;
    // System.out.println(count);
     for(int i = a; !decoder.endOf(i); decode());
     handler.endElement("", "", na);
 }

 private int decodeStructuredType(int tag)
     throws ASN1Exception
 {
     int id = -1;
     switch(tag)
     {
     case 16: // '\020'
         id = decoder.decodeSequence();
         
         break;

     case 17: // '\021'
         id = decoder.decodeSet();
         break;

     case 8: // '\b'
         decoder.nextIsImplicit(8);
         id = decoder.decodeSequence();
         break;

     default:
         throw new ASN1Exception("Unknown tag: " + tag);
     }
     return id;
 }

 private void decodeOther()
     throws ASN1Exception, SAXException
 {
     int ta = decoder.peekNextTag();
     String na = toClassName(ASN1Tag.getTagClass(ta));
     AttributesImpl aa = new AttributesImpl();
     aa.addAttribute("", "", "tagNum", "CDATA", String.valueOf(ASN1Tag.getTagNumber(ta)));
     if(decoder.nextTagConstructed())
     {
         aa.addAttribute("", "", "valueEncoding", "CDATA", "CONSTRUCTED");
         handler.startElement("", "", na, aa);
         decoder.nextIsImplicit(ta);
         for(int i = decoder.decodeSequence(); !decoder.endOf(i); decode());
     } else
     {
         aa.addAttribute("", "", "valueEncoding", "CDATA", "PRIMITIVE");
         if(encoding != null)
             aa.addAttribute("", "", "encoding", "CDATA", encoding);
         handler.startElement("", "", na, aa);
         String tagNum = aa.getValue("tagNum");
         decoder.nextIsImplicit(ta);
         byte bb[] = decoder.decodeOctetString();
         char cc[] = Hex.toString(bb).toCharArray();
         if(encoding != null)
             cc = encodeOctetString(cc, encoding);
         handler.characters(cc, 0, cc.length);
     }
     handler.endElement("", "", na);
 }

 private String toClassName(int tagCl)
     throws ASN1Exception
 {
     String name = null;
     switch(tagCl)
     {
     case 0: // '\0'
         name = "_UNIVERSAL";
         break;

     case 1: // '\001'
         name = "_APPLICATION";
         break;

     case 2: // '\002'
         name = "_CONTEXT";
         break;

     case 3: // '\003'
         name = "_PRIVATE";
         break;

     default:
         throw new ASN1Exception("Unknown tag class: " + tagCl);
     }
     return name;
 }

 private static final String EMPTY = "";
 private ContentHandler handler;
 private boolean isPartial;
 private String encoding;
 private BERDecoder decoder;
 private InputStream in;
 private byte data[];
}
